package edu.ut.ece382c.valloy.converter;

import edu.ut.ece382c.valloy.hierarchy.Node;
import edu.ut.ece382c.valloy.hierarchy.StringTree;
import edu.ut.ece382c.valloy.inputter.Inputter;

public class StrConverter
{
	private String[][] my4arrays;
	private StringTree myTree;
	
	public static void main(String[] args)
	{
		StrConverter myConverter = new StrConverter("src/converter/test_input.txt");
		System.out.println(myConverter.classes());
		System.out.println(myConverter.functions());
		System.out.println(myConverter.predicates());
		System.out.println(myConverter.everythingElse());
	}
	
	public StrConverter(String pathToFile)
	{
		myTree = new StringTree();
		my4arrays = new Inputter(pathToFile).getMy4Arrays();	
		myTree = StringTree.recursivelyGenerateHierarchy(myTree, my4arrays[0]);
		my4arrays = convertSuper();
	}	
	
	public String classes()
	{
		return(generateObjectAndClassCode());
	}
	
	private String generateObjectAndClassCode()
	{
		String finalString = "abstract sig Class { ext: lone Class }\n\none sig ";
		for (Node<String> aNode:myTree.toList())
			finalString += aNode.getData() + "_Class, ";
		finalString = finalString.substring(0, finalString.length() - 2) + " extends Class {}\n\nfact Hierarchy {\nno " + myTree.getRootElement().getData() + "_Class.ext\n"; 
		for (Node<String> aNode:myTree.toList())
		{
			if (aNode.getData().equals(myTree.getRootElement().getData()))
				continue;
			finalString += aNode.getData() + "_Class.ext = " + StringTree.getParentNodeNameByChildNodeName(aNode.getData(), myTree) + "_Class\n";
		}
		finalString += "}\n\none sig " + myTree.getRootElement().getData() + " {\nclass: one Class\n" + getClassBody(myTree.getRootElement().getData()) + "\n}\n\n";
		for (Node<String> aNode:myTree.toList())
		{
			if (aNode.getData().equals(myTree.getRootElement().getData()))
				continue;
			finalString += "sig " + aNode.getData() + " extends " + StringTree.getParentNodeNameByChildNodeName(aNode.getData(), myTree) + " {\n" + getClassBody(aNode.getData()) + "\n}\n\n";
		}
		finalString += "fun " + myTree.getRootElement().getData() + ".getClass[]: Class { this.Class }\n\nfact objectClasses {\n";
		for (Node<String> aNode:myTree.toList())
		{
			if (aNode.getChildren().size() > 0)
			{
				finalString += "(" + aNode.getData();
				for (Node<String> aChildNode:aNode.getChildren())
					finalString += " - " + aChildNode.getData();
				finalString += ").class = " + aNode.getData() + "_Class\n";
			}
			else
				finalString += aNode.getData() + ".class = " + aNode.getData() + "_Class\n";
		}
		finalString += "}\n";
		return finalString;
	}
	
	public String predicates()
	{
		return my4arrays[2] == null ? "":getFunctionsOrPredicates("pred", my4arrays[2]);
	}
	
	public String functions()
	{
		return my4arrays[1] == null ? "":getFunctionsOrPredicates("fun", my4arrays[1]);
	}
	
	private String getFunctionsOrPredicates(String flag, String[] functionsOrPredicates)
	{
		String finalString = "";
		String objectPart = "";
		String functionPart = "";
		String parameters = "";
		String body = "";
		for (String aVirtualFunctionOrPredicate:functionsOrPredicates)
		{
			if (aVirtualFunctionOrPredicate.contains("["))
			{
				functionPart = aVirtualFunctionOrPredicate.substring(aVirtualFunctionOrPredicate.indexOf(".")+1, aVirtualFunctionOrPredicate.indexOf("["));
				parameters = aVirtualFunctionOrPredicate.substring(aVirtualFunctionOrPredicate.indexOf("[")+1, aVirtualFunctionOrPredicate.indexOf("]"));
			}
			else
				functionPart = aVirtualFunctionOrPredicate.substring(aVirtualFunctionOrPredicate.indexOf(".")+1, aVirtualFunctionOrPredicate.indexOf("{"));				
			body = aVirtualFunctionOrPredicate.substring(aVirtualFunctionOrPredicate.indexOf("{")+1, aVirtualFunctionOrPredicate.indexOf("}"));
			objectPart = aVirtualFunctionOrPredicate.substring(aVirtualFunctionOrPredicate.indexOf(flag) + flag.length() + 1, aVirtualFunctionOrPredicate.indexOf("."));
			finalString += "pred " + StringTree.highestNodeInHierarchyThatDefinesFunction(functionPart, myTree, functionsOrPredicates) + "." + objectPart + "_" + functionPart + "[" + parameters + "] {\n" + body + "\n}\n\n";
			if (objectPart.equals(StringTree.highestNodeInHierarchyThatDefinesFunction(functionPart, myTree, functionsOrPredicates)))
			{
				finalString += "pred " + objectPart + "." + functionPart + "[" + parameters + "] {\nthis.class = " + objectPart + "_Class => this." + objectPart + "_" + functionPart + "[" + parameters + "]";
				for (Node<String> aNode:StringTree.getAllDescendantsOfNode(objectPart, myTree))
					finalString += " else\nthis.class = " + aNode.getData() + "_Class => this." + StringTree.getNearestParentNodeThatDefinesFunction(aNode, functionPart, myTree, functionsOrPredicates) + "_" + functionPart + "[" + parameters + "]";
				finalString += "\n}\n\n";
			}
		}
		return finalString;
	}
	
	public String everythingElse()
	{
		return my4arrays[3][0];
	}
	
	private String getClassBody(String className)
	{
		for (String aClass:my4arrays[0])
			if (aClass.contains(" extends ") ? aClass.split("extends")[0].contains(className) : aClass.split("\\{")[0].contains(className))
				return aClass.split("\\{")[1].trim().length() > 1 ? aClass.split("\\{")[1].split("\\}")[0].trim() : "";
		return null;
	}
			
	private String[][] convertSuper()
	{
		for (int i = 0; i < this.my4arrays[0].length; i++)
			my4arrays[0][i] = my4arrays[0][i].replace("super", my4arrays[0][i].substring(my4arrays[0][i].indexOf("extends ")+8, my4arrays[0][i].indexOf("{")).trim());
				
		if (my4arrays[1] != null)
		{
			for (int j = 0; j < my4arrays[1].length; j++)
			{
				String parentNodeName = StringTree.getParentNodeNameByChildNodeName(my4arrays[1][j].substring(my4arrays[1][j].indexOf("fun ")+4, my4arrays[1][j].indexOf(".")), myTree);
				if(parentNodeName != null)
					my4arrays[1][j] = my4arrays[1][j].replace("super", parentNodeName);			
			}
		}
		if (my4arrays[2] != null)
		{
			for (int k = 0; k < my4arrays[2].length; k++)
			{
				String parentNodeName = StringTree.getParentNodeNameByChildNodeName(my4arrays[2][k].substring(my4arrays[2][k].indexOf("pred ")+4, my4arrays[2][k].indexOf(".")), myTree);
				if(parentNodeName != null)
					my4arrays[2][k] = my4arrays[2][k].replace("super", parentNodeName);	
			}
		}
		return my4arrays;
	}	
}
